
#include <iostream>
#include <fstream>
#include <boost/scoped_ptr.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/filesystem.hpp>
#include "package.h"
#include "DebugStream.h"

#ifdef SERVER
#include "Device.h"
#include "Session.h"
#endif

using namespace std;
using namespace boost;
using namespace boost::asio;

namespace Engine {

string CPackage::NULL_STRING = string("");

CPackage::CPackage(void)
{
}

CPackage::CPackage(boost::asio::streambuf& data)
{
	istream is(&data);
	ParseProperties(is);

	// Content
	ostream os(&mContent);
	os << is.rdbuf();
	mContent.commit(mContent.size());

	int content_size = GetPropertieInt(string(PACKAGE_PROP_CONTENT_LENGTH));
	if(mContent.size() < content_size) {
		throw CPackageParseException() << parse_error("Invalid end of content");
	}
}

CPackage::CPackage(boost::shared_ptr<std::istream> is)
{
	ParseProperties(*is);
	mContentInputStream = is;
}

CPackage::~CPackage(void)
{
}

void CPackage::ParseProperties(std::istream& is)
{
	string line;
		
	// Properties
	while(1) {

		getline(is, line, '\n');

		if(line.length() == 0 || line[line.length()-1] != '\r') {
			throw CPackageParseException() << parse_error("Invalid end of line : " + line);
		}

		algorithm::trim(line);

		if(line.length() == 0) break;

		int pos = line.find(':');
		if(pos <= 0) {
			throw CPackageParseException() << parse_error("Invalid properties line : " + line);
		}

		string name = line.substr(0, pos);
		algorithm::trim_left(name);
		string value = line.substr(pos+1);
		algorithm::trim_left(value);

		AddPropertie(name, value);
		//DebugStream << "Request propertie - " << s[0] << ": " << value << "\n";
	}

	for(map<string, string>::iterator i = mProperties.begin(); i != mProperties.end(); i++) {
		DebugStream << "[PROP] " << i->first << '=' << i->second << '\n';
	}
}

void CPackage::SkipContent()
{
	if(!mContentInputStream) return;
	int size = GetPropertieInt(string(PACKAGE_PROP_CONTENT_LENGTH));
	if(size <= 0) return;

	char buf[1024];
	int n = size < sizeof(buf) ? size : sizeof(buf);
	while(size > 0) {
		mContentInputStream->read(buf, n);
		size -= n;
	}

}

bool CPackage::SaveContent(const std::string& file_path)
{
	
	int size = GetPropertieInt(string(PACKAGE_PROP_CONTENT_LENGTH));
	if(size == 0) return false;

	bool ret = true;

	boost::shared_ptr<std::istream> is = GetContentInputStream();
	std::ofstream os(file_path, ios::binary);

	DebugStream << "CPackage::SaveContent - \n";

	try {
		char buf[4096];
		int saved = 0;
		while(saved < size) {
			int n = size - saved;
			if(n > sizeof(buf)) n = sizeof(buf);
			is->read(buf, n);
			os.write(buf, n);
			saved += n;
			DebugStream << saved << '/' << size << '\r';
		}

	}
	catch(std::exception& e) {
		DebugStream << "CPackage::SaveContent - error : " << e.what() << '\n';
		ret = false;
	}

	DebugStream << '\n';

	os.flush();
	os.close();

	return ret;
}

void CPackage::GetData(ostream& os)
{
	for(map<string, string>::iterator i = mProperties.begin(); i != mProperties.end(); i++)
	{
		os << i->first;
		os << ": ";
		os << i->second;
		os << "\r\n";
	}

	os << "\r\n";

	DebugStream << " Build conetent,  size = " << (int)mContentSize << '\n';

	/*
	boost::shared_ptr<istream> cis = GetContentInputStream();
	std::ios::pos_type remain = mContentSize;
	char buf[4096];
	while(remain > 0) {
		int n = remain < sizeof(buf) ? remain : sizeof(buf);
		cis->read(buf, n);
		os.write(buf, n);
		remain -= n;
		DebugStream << (int)remain << '\r';
	}

	DebugStream << '\n';
	*/

	boost::shared_ptr<istream> cis = GetContentInputStream();
	if(cis) os << cis->rdbuf();
}

boost::shared_ptr<istream> CPackage::GetContentInputStream() { 
	if(mContentInputStream) {
		return mContentInputStream;
	}
	else {
		if(mContent.size() > 0) { 
			mContent.commit(mContent.size());
			return boost::shared_ptr<istream>(new istream(&mContent));
		}
		else
		if(mContentFileName.length() > 0) {
			return boost::shared_ptr<istream>(new ifstream(mContentFileName, ios::binary));
		}
		else {
			return boost::shared_ptr<istream>();
		}
	}
}

void CPackage::GetContentAsString(string& s)
{
	Engine::DebugStream << "CPackage::GetContentAsString In\n";

	int size = GetPropertieInt(string(PACKAGE_PROP_CONTENT_LENGTH));
	if(size == 0) return;

	boost::shared_ptr<std::istream> is = GetContentInputStream();	
	if(!is) return;
	
	boost::scoped_ptr<char> buf(new char[size+1]);
	is->read(buf.get(), size);
	buf.get()[size] = 0;
	s = buf.get();
	/*
	s.reserve(size+1);
	while(size > 0) {
		int n = is->readsome(buf.get(), size);	
		buf.get()[n] = 0;
		s.append(buf.get());
		size -= n;
		Engine::DebugStream << "CPackage::GetContentAsString read : " << n << "left : " << size << '\n';
		Engine::DebugStream << s << '\n';
	}
	*/

}

boost::shared_ptr<rapidjson::Document> CPackage::ParseContentAsJson()
{
	boost::shared_ptr<std::istream> is = GetContentInputStream();
	if(!is) return boost::shared_ptr<rapidjson::Document>();
	std::stringstream ss;
	ss << is->rdbuf();

	DebugStream << "Paser json : " << ss.str() << '\n';
	boost::shared_ptr<rapidjson::Document> doc(new rapidjson::Document());

	if(doc->Parse<0>((char*)ss.str().c_str()).HasParseError()) {
		DebugStream << doc->GetParseError() << "\r\n";
		doc.reset();
	}

	return doc;
}

void CPackage::SetContent(string& content, string& type)
{
	*(GetContentOutputStream()) << content;
	AddPropertie(string(PACKAGE_PROP_CONTENT_TYPE), type);
	mContentSize = mContent.size();
	AddPropertie(string(PACKAGE_PROP_CONTENT_LENGTH), boost::lexical_cast<string>(mContentSize));
}

void CPackage::SetContentFile(string& filename, string& type)
{
	mContentFileName = filename;
	AddPropertie(string(PACKAGE_PROP_CONTENT_TYPE), type);

	boost::filesystem::path file = mContentFileName;
	mContentSize = boost::filesystem::file_size(file);
	AddPropertie(
		string(PACKAGE_PROP_CONTENT_LENGTH),
		boost::lexical_cast<string>(mContentSize)
		);
}

}